package com.trgis.yhmap.util.wmtsproxy.controller;

import com.trgis.yhmap.util.wmtsproxy.WmtsXMLReader;
import com.trgis.yhmap.util.wmtsproxy.WMTSProxyConfiguration;
import com.trgis.yhmap.util.wmtsproxy.exception.WmtsConfigException;
import com.trgis.yhmap.util.wmtsproxy.service.WMTSService;
import com.trgis.yhmap.util.wmtsproxy.vo.LayersVO;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.HttpClients;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 2021年2月3日 重写代理WMTS服务
 * 1.主要代理第三方WMTS的获取瓦片服务，可配置服务地址、服务名称、瓦片层级等参数
 * 2.代理本地geoserver服务，根据图层名称获取图层服务信息
 * 3.读取瓦片配置文件信息，主要可用来判断请求是否超过瓦片范围，配置调用参数
 * @author 王风雨
 * 2021年12月15日 增加可代理Rest风格的瓦片地址，格式为WMTS Capabilities.xml 中 </br>
 * <ResourceURL format="image/png" resourceType="tile" template="http://localhost:8081/wmtsproxy/wmts/rest/test/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}?format=image/png"/>
 */
@RestController
@CrossOrigin(origins = "*")
@RequestMapping(value = "/wmts", method = RequestMethod.GET)
public class WMTSController {
    private static final Logger log = LoggerFactory.getLogger(WMTSController.class);

    @Autowired
    WMTSProxyConfiguration wmtsProxyConfiguration;

    @Autowired
    WMTSService wmtsService;

    private WmtsXMLReader reader = WmtsXMLReader.getReader();

    /**
     * 常规的WMTS服务瓦片调用及参数
     * @param layer
     * @param tilematrixset
     * @param tilematrix
     * @param tilerow
     * @param tilecol
     * @return
     */
    @GetMapping(params = "REQUEST=GetTile", produces = {"image/png", "image/jpg", "image/jpeg", "text/xml", "application/octet-stream"})
    public ResponseEntity<byte[]> getTile(@RequestParam(name = "LAYER") String layer,
                          @RequestParam(name = "TILEMATRIXSET") String tilematrixset,
                          @RequestParam(name = "TILEMATRIX") String tilematrix,
                          @RequestParam(name = "TILEROW") String tilerow,
                          @RequestParam(name = "TILECOL") String tilecol,
                          HttpServletRequest request) throws URISyntaxException {

        try {
            return ResponseEntity.ok(wmtsService.getTile(layer, request));
        } catch (WmtsConfigException e) {
            switch (e.getMsg()){
                case notFoundLayer :
                    log.info("配置文件中没有配置这个图层的对应信息:{}", layer);
                    break;
                case notFoundUrl :
                    log.info("没有配置对应的wmts瓦片地址:{}", layer);
                    break;
                case notFoundMatrix:
                    log.info("没有配置对应的matrixSet:{}",e.getInfoStr());
                    break;
                case tileOutOfRange:
//                    log.error(e.getInfoStr());
                    break;
                case remoteException:
                    break;
            }
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .contentType(MediaType.TEXT_XML)
                    .body(toUserInfo(e.getInfoStr()).getBytes());
        }
    }

    @GetMapping(path = "/{layer}",params = "REQUEST=GetTile", produces = {"image/png", "image/jpg", "image/jpeg", "text/xml", "application/octet-stream"})
    public ResponseEntity<byte[]> getTile2(@PathVariable String layer,
                           @RequestParam(name = "TILEMATRIXSET") String tilematrixset,
                           @RequestParam(name = "TILEMATRIX") String tilematrix,
                           @RequestParam(name = "TILEROW") String tilerow,
                           @RequestParam(name = "TILECOL") String tilecol,
                           HttpServletRequest request) throws URISyntaxException {
        return getTile(layer, tilematrixset, tilematrix, tilerow, tilecol, request);
    }

    @GetMapping(path = "/{layer}",params = "request=GetTile", produces = {"image/png", "image/jpg", "image/jpeg", "text/xml", "application/octet-stream"})
    public ResponseEntity<byte[]> getTile3(@PathVariable String layer,
                           @RequestParam(name = "tilematrixset", required = false) String tilematrixset,
                           @RequestParam(name = "tilematrix", required = false) String tilematrix,
                           @RequestParam(name = "tilerow", required = false) String tilerow,
                           @RequestParam(name = "tilecol", required = false) String tilecol,
                           HttpServletRequest request) throws URISyntaxException {
        return getTile(layer, tilematrixset, tilematrix, tilerow, tilecol, request);
    }

    @GetMapping(path = "/{layer}",params = "Request=GetTile", produces = {"image/png", "image/jpg", "image/jpeg", "text/xml", "application/octet-stream"})
    public ResponseEntity<byte[]> getTile32(@PathVariable String layer,
                                    @RequestParam(name = "tilematrixset", required = false) String tilematrixset,
                                    @RequestParam(name = "tilematrix", required = false) String tilematrix,
                                    @RequestParam(name = "tilerow", required = false) String tilerow,
                                    @RequestParam(name = "tilecol", required = false) String tilecol,
                                    HttpServletRequest request) throws URISyntaxException {
        return getTile(layer, tilematrixset, tilematrix, tilerow, tilecol, request);
    }

    @GetMapping(params = "request=GetTile", produces = {"image/png", "image/jpg", "image/jpeg", "text/xml", "application/octet-stream"})
    public ResponseEntity<byte[]> getTile4(@RequestParam(name = "layer") String layer,
                           @RequestParam(name = "tilematrixset", required = false) String tilematrixset,
                           @RequestParam(name = "tilematrix", required = false) String tilematrix,
                           @RequestParam(name = "tilerow", required = false) String tilerow,
                           @RequestParam(name = "tilecol", required = false) String tilecol,
                           HttpServletRequest request) throws URISyntaxException {
        return getTile(layer, tilematrixset, tilematrix, tilerow, tilecol, request);
    }
    
    @GetMapping(params = "Request=GetTile", produces = {"image/png", "image/jpg", "image/jpeg", "text/xml", "application/octet-stream"})
    public ResponseEntity<byte[]> getTile5(@RequestParam(name = "layer") String layer,
                           @RequestParam(name = "tilematrixset", required = false) String tilematrixset,
                           @RequestParam(name = "tilematrix", required = false) String tilematrix,
                           @RequestParam(name = "tilerow", required = false) String tilerow,
                           @RequestParam(name = "tilecol", required = false) String tilecol,
                           HttpServletRequest request) throws URISyntaxException {
        return getTile(layer, tilematrixset, tilematrix, tilerow, tilecol, request);
    }

    // rest风格获取瓦片 http://127.0.0.1:8080/geoserver/gwc/service/wmts/rest/lpemapanno/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}?format=image/png
    @GetMapping(value = "/rest/{layer}/{style}/{tileMatrixSet}/{tileMatrix}/{tileRow}/{tileCol}", produces = {"image/png", "image/jpg", "image/jpeg", "text/xml", "application/octet-stream"})
    public ResponseEntity<byte[]> getTileRest(@PathVariable String layer,
                                              @PathVariable String style,
                                              @PathVariable String tileMatrixSet,
                                              @PathVariable String tileMatrix,
                                              @PathVariable int tileRow,
                                              @PathVariable int tileCol,
                                              @RequestParam(defaultValue = "image/png", required = false) String format){
        try {
            return ResponseEntity.ok(wmtsService.getTileRest(layer, style, tileMatrixSet, tileMatrix, tileRow, tileCol, format));
        } catch (WmtsConfigException e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .contentType(MediaType.TEXT_XML)
                    .body(toUserInfo(e.getInfoStr()).getBytes());
        }
    }
    /**
     * 常规方式获取WMTS服务图层信息
     * @param layer 指定的图层
     * @return
     */
    @Cacheable(cacheNames = "xml")
    @GetMapping(params = "REQUEST=GetCapabilities", produces = "application/xml;charset=UTF-8")
    public String getCapabilitiesFromUrl(@RequestParam(name = "LAYER") String layer, HttpServletRequest request){
        log.info("获取图层 : {} 的服务信息" , layer);
        if (Optional.ofNullable(reader).isPresent()){
            try {
                return reader.getCapabilities(layer);
            } catch (WmtsConfigException e) {
                switch (e.getMsg()){
                    case notFoundLayer:
                        return toUserInfo("没有发现图层的相关配置，请联系管理员");
                    case notFoundCapabilities:
                        return getCapabilitiesFromUrl(layer, reader.getServerUrl(layer), request.getRequestURL().toString());
                    default:
                        return toUserInfo("读取服务配置信息发生错误，请联系管理员");
                }
            }
        }
        return toUserInfo("找不到相关服务配置信息");
    }

    @Cacheable(cacheNames = "xml")
    @GetMapping(path = "/{layer}", params = "REQUEST=GetCapabilities", produces = "application/xml;charset=UTF-8")
    public String getCapabilitiesFromUrl2(@PathVariable String layer, HttpServletRequest request){
        return getCapabilitiesFromUrl(layer, request);
    }
    @Cacheable(cacheNames = "xml")
    @GetMapping(path = "/{layer}", params = "request=GetCapabilities", produces = "application/xml;charset=UTF-8")
    public String getCapabilitiesFromUrl3(@PathVariable String layer, HttpServletRequest request){
        return getCapabilitiesFromUrl(layer, request);
    }
    @Cacheable(cacheNames = "xml")
    @GetMapping(path = "/{layer}", produces = "application/xml;charset=UTF-8")
    public String getCapabilitiesFromUrl4(@PathVariable String layer, HttpServletRequest request){
        return getCapabilitiesFromUrl(layer, request);
    }

    /**
     * 更改wmtsCon.xml配置文件后，重新读取配置内容，清除缓存
     * @param who 当日几号，如20
     */
    @GetMapping(path = "/refresh")
    @CacheEvict(cacheNames = {"xml", "tile", "all"}, allEntries = true)
    public String refresh(@RequestParam int who){
        if (LocalDate.now().getDayOfMonth() == who){
            log.info("重新读取配置文件wmtsCon.xml");
            wmtsProxyConfiguration.refresh();
            return "刷新配置并清除缓存完成";
        }
        return "今天几号？";
    }

    @GetMapping(path = "/all")
    @Cacheable(cacheNames = "all")
    public LayersVO getAllLayers(){
        return reader.getAllLayers();
    }

    /**
     * 获取指定图层服务的GetCapabilities信息
     * @param layer
     * @param url 图层服务的真实地址
     * @param proxyUrl 本代理的地址
     * @return
     */
    private String getCapabilitiesFromUrl(String layer, String url, String proxyUrl){
        String realLayer = reader.gerRealLayer(layer);
        URI uri ;
        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            uriBuilder.setParameter("REQUEST", "GetCapabilities")
                      .setParameter("SERVER", "WMTS")
                      .setParameter("LAYER", realLayer);
            uri = uriBuilder.build();
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return toUserInfo("获取服务信息失败，配置的服务网络地址出现问题");
        }

        Document doc = null;
        try (CloseableHttpResponse response = HttpClients.createDefault().execute(new HttpGet(uri))){
            if (HttpStatus.OK.value() == response.getStatusLine().getStatusCode()) {
                SAXReader saxReader = new SAXReader();
                doc = saxReader.read(response.getEntity().getContent());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return toUserInfo("读取信息发生错误，请联系管理员");
        }

        StringWriter stringWriter = new StringWriter();
        Optional.ofNullable(doc).ifPresent(document -> {
            Element rootElement = document.getRootElement();
            Element contents = rootElement.element("Contents");
            List<Element> lys = contents.elements("Layer");
            List<Element> tileMatrixSets = contents.elements("TileMatrixSet");
            List<String> links = new ArrayList<>();
            // 获取layer节点下的 <ows:Identifier>  和 <TileMatrixSetLink><TileMatrixSet>
            // 不是指定图层的删除
            for (Iterator<Element> iterator = lys.iterator(); iterator.hasNext();){
                Element lay = iterator.next();
                if (realLayer.equals(lay.element("Identifier").getText())){
                    lay.element("Identifier").setText(layer);
                    List<Element> tileMatrixSetLinks = lay.elements("TileMatrixSetLink");
                    links = tileMatrixSetLinks.stream().map(link ->
                        link.element("TileMatrixSet").getText()
                    ).collect(Collectors.toList());
                } else {
                    iterator.remove();
                }
            }

            for (Iterator<Element> iterator = tileMatrixSets.iterator(); iterator.hasNext();){
                Element tileMatrixSet = iterator.next();
                if (links.indexOf(tileMatrixSet.elementText("Identifier")) < 0){
                    iterator.remove();
                }
            }

            if (!lys.isEmpty()) {
                try {
                    stringWriter.write(reader.changeSettings(layer, document, proxyUrl));
                } catch (IOException e) {
                    e.printStackTrace();
                    stringWriter.write(toUserInfo("获取图层配置信息时发生错误，请联系管理员"));
                }
            } else {
                stringWriter.write(toUserInfo("没有相关图层的信息，请联系管理员"));
            }
        });

        return stringWriter.toString();
    }

    private String toUserInfo(String msg){
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<info>" + msg + "</info>";
    }
}
